#pragma once

#include "stdafx.h"

#include <map>
#include <memory>
#include "Geo.h"
#include "TpqMap.h"

using namespace std;
using namespace Geo;
using namespace Tpq;

namespace Tpq
{
	struct RenderedMapletKeyComparer
	{
		inline bool operator () (const RenderedMapletKey& first, const RenderedMapletKey& second)
		{
			return first.id < second.id || (first.id == second.id && (first.row < second.row || (first.row==second.row && first.column<second.column)));
		}
	};

	typedef multimap<RenderedMapletKey, shared_ptr<RenderedMaplet>, RenderedMapletKeyComparer> RenderedMapletMap;

	class TpqMapManager
	{
	private:
		static TpqMapManager* instance;

	private:
		int moveStep;
		map<string, string> mapCatalogs;
		map<MapId, shared_ptr<TpqMap>> maps;
		RenderedMapletMap renderedMaplets;
		queue<RenderedMapletMap::value_type> pendingMaplets;

		int mapLevel;						// Current map level
		pair<double, double> scale;			// Current scale (pixel per second)
		LatLongCoord center;				// Center of the view area (in seconds)
		pair<double, double> nativeScale;	// Native scale at the center
		vector<vector<double>> presetScales;

		string GetFilePath(MapId id) const;						// Locate the tpq file based on the map id
		GSize<double> GetMapSize(int level) const;				// Get the dimension of a quad in world coordinates based on the map level.
		MapId LookupMapId(LatLongCoord coord, int level) const;	// Look up map id for a coordinate and map level. Biased towards northwest.
		shared_ptr<TpqMap> LoadMap(CDC& dc, MapId id);			// Load a map if it's not already loaded.
		double PickClosestScale(int level, double x);			// Pick a closest scale from the preset list.
		double GetNextScale(double scale, int step);			// Calculate the next level of y scale.
		void Draw(CDC& dc, CRect drawAreaScreen, pair<double,double> scale, bool loadCacheOnly=false);

	public:
		TpqMapManager(void);
		static TpqMapManager& Instance();
		void Draw(CDC& dc, CRect drawAreaScreen);
		void PreloadCache(CDC& dc, CRect drawAreaScreen);	// Predict what will be rendered next and load into memory.
		void DrawUTMGrids(CDC& dc, GRect<double> rectWorld, Transformation wolrdToScreen) const;
		shared_ptr<RenderedMaplet> FindMapletInCache(RenderedMapletMap::key_type key, double xScale, double yScale) const;
		void PutRenderedMapletInCache(RenderedMapletMap::key_type key, RenderedMapletMap::mapped_type value);
		void PutPendingMapletInQueue(RenderedMapletMap::key_type key, RenderedMapletMap::mapped_type value);
		void Initialize(CDC& dc, LatLongCoord center, double desiredScale);
		void Zoom(int step);
		void Pan(CDC& dc, int xStep, int yStep);
		void OnIdle(CView& view);
		void RenderPendingMaplets(CDC& dc);
	};
}